/**
 * 
 */
package org.ljm.basic.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.ljm.basic.model.Pager;
import org.ljm.basic.model.SystemContext;

/**
 * @author ljm
 *
 */
@SuppressWarnings("unchecked")
public class BaseDao<T> implements IBaseDao<T> {

	@Resource
	private SessionFactory sessionFactory;
	
	//用来获取泛型的class
	private Class<?> clz;
	
	protected Session getSession(){
		return sessionFactory.getCurrentSession();
	}
	
	protected Class<?> getClz(){
		if(clz == null){
			Type genType = getClass().getGenericSuperclass();
			Type[] params = ((ParameterizedType)genType).getActualTypeArguments();
			clz = (Class<?>) params[0];
		}
		return clz;
	}
	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#add(java.lang.Object)
	 */
	@Override
	public T add(T t) {
		getSession().save(t);
		return t;
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#update(java.lang.Object)
	 */
	@Override
	public void update(T t) {
		getSession().update(t);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#delete(int)
	 */
	@Override
	public void delete(int id) {
		getSession().delete(load(id));
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#load(int)
	 */
	@Override
	public T load(int id) {
		return (T) getSession().load(getClz(), id);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#list(java.lang.String, java.lang.Object[], java.util.Map)
	 */
	@Override
	public List<T> list(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		Query query = getSession().createQuery(hql);
		setParameters(args, query);
		setAliasParameters(alias, query);
		return query.list();
	}

	private String initSort(String hql) {
		String sort = SystemContext.getSort();
		String order = SystemContext.getOrder();
		if(sort != null && !"".equals(sort.trim())){
			hql += " order by "+sort;
			if(!"desc".equals(order))
				hql += " asc";
			else
				hql += " desc";
		}
		return hql;
	}

	private void setParameters(Object[] args, Query query) {
		if(args != null && args.length > 0){
			int index = 0;
			for(Object arg : args){
				query.setParameter(index++, arg);
			}
		}
	}

	@SuppressWarnings("rawtypes")
	private void setAliasParameters(Map<String, Object> alias, Query query) {
		if(alias != null){
			for(Map.Entry<String, Object> map : alias.entrySet()){
				String key = map.getKey();
				Object val = map.getValue();
				if(val instanceof Collection){
					query.setParameterList(key, (Collection) val);
				}else{
					query.setParameter(key, val);
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listByAlias(java.lang.String, java.util.Map)
	 */
	@Override
	public List<T> listByAlias(String hql, Map<String, Object> alias) {
		return this.list(hql,null,alias);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#list(java.lang.String, java.lang.Object[])
	 */
	@Override
	public List<T> list(String hql, Object[] args) {
		return this.list(hql,args,null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#list(java.lang.String, java.lang.Object)
	 */
	@Override
	public List<T> list(String hql, Object arg) {
		return this.list(hql,new Object[]{arg});
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#list(java.lang.String)
	 */
	@Override
	public List<T> list(String hql) {
		return this.list(hql,null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#find(java.lang.String, java.lang.Object[], java.util.Map)
	 */
	@Override
	public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		String cq = getCountHql(hql, true);
		Query query = getSession().createQuery(hql);
		Query cquery = getSession().createQuery(cq);
		//设置别名参数
		setAliasParameters(alias, query);
		setAliasParameters(alias, cquery);
		//设置？参数
		setParameters(args, query);
		setParameters(args, cquery);
		Pager<T> pager = new Pager<T>();
		//设置分页参数
		setPagers(query, pager);
		List<T> data = query.list();
		pager.setData(data);
		long total = (Long) cquery.uniqueResult();
		pager.setTotal(total);
		return pager;
	}

	private void setPagers(Query query, Pager<?> pager) {
		Integer offset = SystemContext.getOffset();
		if(offset == null || offset < 0) offset = 0;
		pager.setOffset(offset);
		Integer size = SystemContext.getSize();
		if(size == null || size < 0) size = 10;
		pager.setSize(size);
		query.setFirstResult(offset).setMaxResults(size);
	}

	private String getCountHql(String hql, boolean isHql) {
		String str = hql.substring(hql.indexOf("from"));
		str = "select count(*) "+str;
		if(isHql)
			str.replaceAll("fecth", "");
		return str;
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findByAlias(java.lang.String, java.util.Map)
	 */
	@Override
	public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
		return this.find(hql,null,alias);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#find(java.lang.String, java.lang.Object[])
	 */
	@Override
	public Pager<T> find(String hql, Object[] args) {
		return this.find(hql,args,null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#find(java.lang.String, java.lang.Object)
	 */
	@Override
	public Pager<T> find(String hql, Object arg) {
		return this.find(hql,new Object[]{arg});
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#find(java.lang.String)
	 */
	@Override
	public Pager<T> find(String hql) {
		return this.find(hql,null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#queryObject(java.lang.String, java.lang.Object[], java.util.Map)
	 */
	@Override
	public Object queryObject(String hql, Object[] args,
			Map<String, Object> alias) {
		Query query = getSession().createQuery(hql);
		setParameters(args, query);
		setAliasParameters(alias, query);
		return query.uniqueResult();
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#queryObjectByAlias(java.lang.String, java.util.Map)
	 */
	@Override
	public Object queryObjectByAlias(String hql, Map<String, Object> alias) {
		return this.queryObject(hql,null,alias);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#queryObject(java.lang.String, java.lang.Object[])
	 */
	@Override
	public Object queryObject(String hql, Object[] args) {
		return this.queryObject(hql,args,null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#queryObject(java.lang.String, java.lang.Object)
	 */
	@Override
	public Object queryObject(String hql, Object arg) {
		return this.queryObject(hql, new Object[]{arg});
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#queryObject(java.lang.String)
	 */
	@Override
	public Object queryObject(String hql) {
		return this.queryObject(hql, null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#updateByHql(java.lang.String, java.lang.Object[])
	 */
	@Override
	public void updateByHql(String hql, Object[] args) {
		Query query = getSession().createQuery(hql);
		setParameters(args, query);
		query.executeUpdate();
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#updateByHql(java.lang.String, java.lang.Object)
	 */
	@Override
	public void updateByHql(String hql, Object arg) {
		this.updateByHql(hql, new Object[]{arg});
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#updateByHql(java.lang.String)
	 */
	@Override
	public void updateByHql(String hql) {
		this.updateByHql(hql, null);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object[], java.util.Map, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>List<N> listBySql(String hql, Object[] args,
			Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		hql = initSort(hql);
		SQLQuery sq = getSession().createSQLQuery(hql);
		setParameters(args, sq);
		setAliasParameters(alias, sq);
		setResultTransform(clz, hasEntity, sq);
		return sq.list();
	}

	private void setResultTransform(Class<?> clz, boolean hasEntity, SQLQuery sq) {
		if(hasEntity)
			sq.addEntity(clz);
		else{
			if("java.util.Map".equals(clz.getName()))
				sq.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
			else
			    sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listByAliasSql(java.lang.String, java.util.Map, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>List<N> listByAliasSql(String hql, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		return this.listBySql(hql, null, alias, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object[], java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>List<N> listBySql(String hql, Object[] args, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(hql, args, null, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>List<N> listBySql(String hql, Object arg, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(hql, new Object[]{arg}, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#listBySql(java.lang.String, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>List<N> listBySql(String hql, Class<?> clz, boolean hasEntity) {
		return this.listBySql(hql, null, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object[], java.util.Map, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>Pager<N> findBySql(String hql, Object[] args,
			Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		String sql = initSort(hql);
		String cql = getCountHql(sql, false);
		SQLQuery sq = getSession().createSQLQuery(sql);
		SQLQuery cq = getSession().createSQLQuery(cql);
		//设置？参数
		setParameters(args, sq);
		setParameters(args, cq);
		//设置别名参数
		setAliasParameters(alias, sq);
		setAliasParameters(alias, cq);
		Pager<N> pager = new Pager<N>();
		//设置分页参数
		setPagers(sq, pager);
		setResultTransform(clz, hasEntity, sq);
		List<N> data = sq.list();
		pager.setData(data);
		long total = ((BigInteger)cq.uniqueResult()).longValue();
		pager.setTotal(total);
		return pager;
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findByAliasSql(java.lang.String, java.util.Map, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>Pager<N> findByAliasSql(String hql, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		return this.findBySql(hql, null, alias, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object[], java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>Pager<N> findBySql(String hql, Object[] args, Class<?> clz,
			boolean hasEntity) {
		return this.findBySql(hql, args, null, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>Pager<N> findBySql(String hql, Object arg, Class<?> clz,
			boolean hasEntity) {
		return this.findBySql(hql, new Object[]{arg}, clz, hasEntity);
	}

	/* (non-Javadoc)
	 * @see org.ljm.basic.dao.IBaseDao#findBySql(java.lang.String, java.lang.Class, boolean)
	 */
	@Override
	public <N extends Object>Pager<N> findBySql(String hql, Class<?> clz, boolean hasEntity) {
		return this.findBySql(hql, null, clz, hasEntity);
	}

}
